/*
 * Copyright (C) 2008 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mylhyl.zxing.scanner.camera;


import com.google.zxing.PlanarYUVLuminanceSource;
import com.mylhyl.zxing.scanner.ScannerOptions;
import com.mylhyl.zxing.scanner.camera.open.CameraFacing;
import com.mylhyl.zxing.scanner.camera.open.OpenCamera;
import com.mylhyl.zxing.scanner.camera.open.OpenCameraInterface;
import com.mylhyl.zxing.scanner.common.Scanner;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.global.configuration.Configuration;
import ohos.media.camera.CameraKit;
import ohos.media.camera.device.CameraAbility;
import ohos.media.camera.device.FrameConfig;

import java.io.IOException;

import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_PREVIEW;

public final class CameraManager {

    private static final int MIN_FRAME_WIDTH = 240;
    private static final int MIN_FRAME_HEIGHT = 240;
    public static final int MAX_FRAME_WIDTH = 1200; // = 5/8 * 1920
    public static final int MAX_FRAME_HEIGHT = 675; // = 5/8 * 1080

    private final Context context;
    private final CameraConfigurationManager configManager;
    private OpenCamera camera;
    private AutoFocusManager autoFocusManager;
    private RectFloat framingRect;
    private RectFloat framingRectInPreview;
    private boolean initialized;
    private boolean previewing;
    private int requestedCameraId = OpenCameraInterface.NO_REQUESTED_CAMERA;
    private float requestedFramingRectWidth;
    private float requestedFramingRectHeight;

    private final PreviewCallback previewCallback;
    private final int statusBarHeight;//状态栏高度
    private int laserFrameTopMargin;
    private ScannerOptions scannerOptions;
    private SurfaceOps mSurfaceOps;

    public CameraManager(Context context, ScannerOptions scannerOptions, SurfaceOps surfaceOps) {
        this.context = context;
        this.mSurfaceOps = surfaceOps;
        this.configManager = new CameraConfigurationManager(context, scannerOptions);
        this.previewCallback = new PreviewCallback(configManager);

        this.statusBarHeight = getStatusBarHeight();
        this.scannerOptions = scannerOptions;
        this.requestedFramingRectWidth = dp2px(scannerOptions.getFrameWidth());
        this.requestedFramingRectHeight = dp2px(scannerOptions.getFrameHeight());
        this.laserFrameTopMargin = dp2px(scannerOptions.getFrameTopMargin());
        setManualCameraId(scannerOptions.getCameraFacing() == CameraFacing.BACK ? 0 : 1);
    }

    public synchronized void openDriver(SurfaceOps holder) throws IOException {
        OpenCamera theCamera = camera;
        if (theCamera == null) {
            //获取手机背面的摄像头
            theCamera = OpenCameraInterface.open(context, requestedCameraId, holder);
            if (theCamera == null) {
                throw new IOException("Camera.open() failed to return object from driver");
            }
            camera = theCamera;
        }

        if (!initialized) {
            initialized = true;
            configManager.initFromCameraParameters(theCamera);
            if (requestedFramingRectWidth > 0 && requestedFramingRectHeight > 0) {
                setManualFramingRect(requestedFramingRectWidth, requestedFramingRectHeight);
                requestedFramingRectWidth = 0;
                requestedFramingRectHeight = 0;
            }
        }
    }

    public synchronized boolean isOpen() {
        return camera != null;
    }

    public synchronized void closeDriver() {
        if (camera != null) {
            camera.getCamera().release();
            camera = null;
            framingRect = null;
            framingRectInPreview = null;
        }
    }

    public synchronized void startPreview() {
        OpenCamera theCamera = camera;
        if (theCamera != null && !previewing) {
            // 获取预览配置模板
            FrameConfig.Builder frameConfigBuilder = theCamera.getCamera().getFrameConfigBuilder(FRAME_CONFIG_PREVIEW);
            // 配置预览Surface
            frameConfigBuilder.addSurface(mSurfaceOps.getSurface());
            FrameConfig previewFrameConfig = frameConfigBuilder.build();
            try {
                // 启动循环帧捕获
                int triggerId = theCamera.getCamera().triggerLoopingCapture(previewFrameConfig);
            } catch (IllegalArgumentException e) {
            } catch (IllegalStateException e) {
            }

            previewing = true;
            autoFocusManager = new AutoFocusManager(theCamera.getCameraKit(), frameConfigBuilder);
        }
    }

    public synchronized void stopPreview() {
        if (autoFocusManager != null) {
            autoFocusManager.stop();
            autoFocusManager = null;
        }
        if (camera != null && previewing) {
            camera.getCamera().stopLoopingCapture();
            previewCallback.setHandler(null, 0);
            previewing = false;
        }
    }

    /**
     * 设置闪光灯
     */
    public synchronized void setTorch(boolean newSetting) {
        OpenCamera theCamera = camera;
        if (theCamera != null && newSetting != configManager.getTorchState(theCamera.getCamera())) {
            boolean wasAutoFocusManager = autoFocusManager != null;
            if (wasAutoFocusManager) {
                autoFocusManager.stop();
                autoFocusManager = null;
            }
            CameraKit cameraKit = theCamera.getCameraKit();
            //获取指定逻辑相机或物理相机的静态能力
            CameraAbility cameraAbility = cameraKit.getCameraAbility(theCamera.getCameraId());
            //获取预览配置模板
            FrameConfig.Builder frameConfigBuilder = theCamera.getCamera().getFrameConfigBuilder(FRAME_CONFIG_PREVIEW);

            configManager.setTorch(cameraAbility, frameConfigBuilder, newSetting);
            if (wasAutoFocusManager) {
                autoFocusManager = new AutoFocusManager(cameraKit, frameConfigBuilder);
                autoFocusManager.start();
            }
        }
    }

    /**
     * 1：将handler与preview回调函数绑定；<br/>
     * 2：注册preview回调函数<br/>
     * 综上，该函数的作用是当相机的预览界面准备就绪后就会调用handler向其发送传入的message
     */
    public synchronized void requestPreviewFrame(EventHandler handler, int message) {
        OpenCamera theCamera = camera;
        if (theCamera != null && previewing) {
            previewCallback.setHandler(handler, message);
        }
    }

    public synchronized RectFloat getFramingRect() {
        if (framingRect == null) {
            if (camera == null) {
                return null;
            }
            Point screenResolution = configManager.getScreenResolution();
            if (screenResolution == null) {
                return null;
            }
            float height;
            float width = findDesiredDimensionInRange(screenResolution.getPointX(), MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
            //竖屏则为正方形
            if (isPortrait()) {
                height = width;
            } else {
                height = findDesiredDimensionInRange(screenResolution.getPointY(), MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
            }
            createFramingRect(width, height, screenResolution);
        }
        return framingRect;
    }

    private static float findDesiredDimensionInRange(float resolution, float hardMin, int hardMax) {
        float dim = 5 * resolution / 8; // Target 5/8 of each dimension
        if (dim < hardMin) {
            return hardMin;
        }
        if (dim > hardMax) {
            return hardMax;
        }
        return dim;
    }

    public synchronized RectFloat getFramingRectInPreview() {
        if (framingRectInPreview == null) {
//            Rect framingRect = getFramingRect();
            RectFloat framingRect = getFramingRect();
            if (framingRect == null) {
                return null;
            }
            RectFloat rect = new RectFloat(framingRect);
            Point cameraResolution = configManager.getCameraResolution();
            Point screenResolution = configManager.getScreenResolution();
            if (cameraResolution == null || screenResolution == null) {
                return null;
            }

            //竖屏识别一维
            if (isPortrait()) {
                rect.left = rect.left * cameraResolution.getPointY() / screenResolution.getPointX();
                rect.right = rect.right * cameraResolution.getPointY() / screenResolution.getPointX();
                rect.top = rect.top * cameraResolution.getPointX() / screenResolution.getPointY();
                rect.bottom = rect.bottom * cameraResolution.getPointX() / screenResolution.getPointY();
            } else {
                rect.left = rect.left * cameraResolution.getPointX() / screenResolution.getPointX();
                rect.right = rect.right * cameraResolution.getPointX() / screenResolution.getPointX();
                rect.top = rect.top * cameraResolution.getPointY() / screenResolution.getPointY();
                rect.bottom = rect.bottom * cameraResolution.getPointY() / screenResolution.getPointY();
            }
            framingRectInPreview = rect;
        }
        return framingRectInPreview;
    }


    public synchronized void setManualCameraId(int cameraId) {
        requestedCameraId = cameraId;
    }

    public synchronized void setManualFramingRect(float width, float height) {
        if (initialized) {
            Point screenResolution = configManager.getScreenResolution();
            if (width > screenResolution.getPointX()) {
                width = screenResolution.getPointX();
            }
            if (height > screenResolution.getPointY()) {
                height = screenResolution.getPointY();
            }

            createFramingRect(width, height, screenResolution);
            framingRectInPreview = null;
        } else {
            requestedFramingRectWidth = width;
            requestedFramingRectHeight = height;
        }
    }

    private void createFramingRect(Float width, Float height, Point screenResolution) {
        float leftOffset = (screenResolution.getPointX() - width) / 2;
        float topOffset = (screenResolution.getPointY() - height) / 2;
        float top = laserFrameTopMargin;
        if (top == 0)
            top = topOffset - statusBarHeight;
        else {
            top += statusBarHeight;
        }
        framingRect = new RectFloat(leftOffset, top, leftOffset + width, top + height);
    }

    public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
        if (scannerOptions.isScanFullScreen()) {
            return new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false);
        }
        RectFloat rect = getFramingRectInPreview();
        if (rect == null) {
            return null;
        }
        return new PlanarYUVLuminanceSource(data, width, height, (int) rect.left, (int) rect.top, (int) rect.getWidth(), (int) rect.getHeight(), false);
    }

    /**
     * 获取状态栏高度
     *
     * @return
     */
    private int getStatusBarHeight() {

        return 129;
    }

    private int dp2px(int dp) {
        return Scanner.dp2px(context, dp);
    }

    public boolean isPortrait() {
        return context.getResourceManager().getConfiguration().DIRECTION_VERTICAL ==
                Configuration.DIRECTION_VERTICAL;
    }

    public Point getScreenResolution() {
        return configManager.getScreenResolution();
    }

    public Point getCameraResolution() {
        return configManager.getCameraResolution();
    }
}
